﻿using System.Drawing;
using System.Drawing.Drawing2D;
using System;

namespace GeoSketch2.Core
{
    /// <summary>
    /// The base class for all object to be drawn to the screen
    /// </summary>
    public abstract class BaseDrawableObject:BaseGeosketchObject
    {
        #region XMLElementsName
#pragma warning disable 1591
        public const string XML_SpecificInfos = "SpecificInfos";
        public const string XML_BaseInfos = "BaseInfos";
        public const string XML_Layer = "Layer";
        public const string XML_Color = "Color";
        public const string XML_LineType = "LineType";
#pragma warning restore 1591
        #endregion

        #region Fields
        /// <summary>
        /// The id of the object
        /// </summary>
        public int Id;

        /// <summary>
        /// The layer of the object
        /// </summary>
        public Layer Layer;

        /// <summary>
        /// The color of the object, Color.empty == Layer Color
        /// </summary>
        public Color _ObjectColor;
        
        /// <summary>
        /// The LineType of the object, null= Layer LineType
        /// </summary>
        public LineType _LineType;

        /// <summary>
        /// True if the object is selected
        /// </summary>
        public bool IsSelected;

        #endregion

        #region Constructor
        /// <summary>
        /// A geosketch drawable object
        /// </summary>
        public BaseDrawableObject()
            : this(new Layer())
        {
        }

        /// <summary>
        /// A geosketch drawable object
        /// </summary>
        public BaseDrawableObject(Layer Layer)
            : this(Layer, Color.Empty, LineTypeByLayer())
        {
        }

        /// <summary>
        /// A geosketch drawable object
        /// </summary>
        public BaseDrawableObject(Layer Layer, Color Color, LineType LineType)
        {
            this.Layer = Layer;
            this._ObjectColor = Color;
            this._LineType = LineType;
            this.IsSelected = false;
            this.Id = Vars.Setup.DrawableObjects.NextAvailableId;
            Vars.Setup.DrawableObjects.NextAvailableId++;
        } 
        #endregion

        #region Methods
        
        /// <summary>
        /// Gets a ByLayer Color object
        /// </summary>
        public static Color ColorByLayer()
        {
            return Color.Empty;
        }

        /// <summary>
        /// Gets a ByLayer LineType object
        /// </summary>
        public static LineType LineTypeByLayer()
        {
            return null;
        }
        
        /// <summary>
        /// The draw method for each object
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        public void Draw(BaseViewport Viewport)
        {
            if (IsSelected)
            {
                DrawSelected(Viewport);
            }
            else
            {
                DrawUnselected(Viewport);
            }
        }

        /// <summary>
        /// The draw method for each object if selected
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        protected abstract void DrawSelected(BaseViewport Viewport);

        /// <summary>
        /// The draw method for each object if not selected
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        protected abstract void DrawUnselected(BaseViewport Viewport);

        /// <summary>
        /// The draw method for each object with a custom pen
        /// </summary>
        /// <param name="Viewport">The viewport to draw to</param>
        /// <param name="CustomPen">The custom pen object to draw the object with</param>
        public abstract void DrawCustomPen(BaseViewport Viewport, Pen CustomPen);

        /// <summary>
        /// The method to test if an object is near to the point clicked, for an object selection purpose
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public abstract bool IsNear(Point2D64D Clic, double IsNearDistance);

        /// <summary>
        /// Returns the nearest line to the click, null if none
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public virtual Line2D64D NearestLine(Point2D64D Clic, double IsNearDistance)
        {
            return null;
        }

        /// <summary>
        /// Returns the nearest point to the click, null if none
        /// </summary>
        /// <param name="Clic">The point clicked in object coordinates</param>
        /// <param name="IsNearDistance">The distance under which the clic is near to the object</param>
        public virtual Point2D64D NearestPoint(Point2D64D Clic, double IsNearDistance)
        {
            return null;
        }

        /// <summary>
        /// Clones the object
        /// </summary>
        public abstract T Clone<T>() where T : class;

        /// <summary>
        /// Translates the object
        /// </summary>
        /// <param name="Vector">The translation vector</param>
        public abstract void Translate(Point2D64D Vector);
        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the color of the object
        /// </summary>
        public Color Color
        {
            get
            {
                if (IsColorByLayer)
                {
                    return Layer.LayerColor;
                }
                else
                {
                    return _ObjectColor;
                }
            }
            set
            {
                _ObjectColor = value;
            }
        }

        /// <summary>
        /// Gets or sets the line type
        /// </summary>
        public LineType LineType
        {
            get
            {
                if (IsLineTypeByLayer)
                {
                    return Layer.LineType;
                }
                else
                {
                    return _LineType;
                }
            }
            set
            {
                _LineType = value;
            }
        }

        /// <summary>
        /// True if the color of the object is the layer's one
        /// </summary>
        public bool IsColorByLayer
        {
            get
            {
                return _ObjectColor.IsEmpty;
            }
        }

        /// <summary>
        /// True if the line type of the object is the layer's one
        /// </summary>
        public bool IsLineTypeByLayer
        {
            get
            {
                return _LineType==null;
            }
        }

        /// <summary>
        /// Gets the pen corresponding to the object
        /// </summary>
        public Pen Pen
        {
            get
            {
                System.Drawing.Pen pen = new Pen(Color, LineType.Width);
                if (LineType.Style != LineStyle.Solid)
                {
                    pen.DashStyle = DashStyle.Custom;
                    pen.DashPattern = LineType.DashPattern;
                }
                return pen;
            }
        }

        /// <summary>
        /// Gets the bounding box of the object, the Y-axis direction of the REctangleD must be upside
        /// </summary>
        public abstract RectangleD Bounds
        {
            get;
        }
        #endregion
    }
}
